#include "Character.h"
#include "EntityCategory.h"
#include <string>
#include <iostream>
#include <sstream>
#include "stdafx.h"



const float K_MAX_SPEED = 6;


Character::Character(b2World* wo, WindowManager* win)
{

	world = wo;
	window = win;

	lockMovement = false;

	//ground->getTop() - height;

	numFootContacts = 0;

	float x = 350;
	float y = 500;

	if(!image.loadFromFile("images/character.png"))
		abort();
	image.createMaskFromColor(sf::Color(255, 0, 255));

	if(!texture.loadFromImage(image))
		abort();

	sprite = sf::Sprite(texture);
	sprite.setPosition(x, y);

	thor::FrameAnimation walk;

	addFrames(walk, 0, 0, 0);
	addFrames(walk, 1, 0, 0);
	addFrames(walk, 2, 0, 0);
	addFrames(walk, 3, 0, 0);

	

	animator.addAnimation("walk", walk, sf::seconds(0.5f));

	thor::FrameAnimation stand;

	addFrames(stand, 0, 1, 1);

	animator.addAnimation("stand", stand, sf::seconds(0.5f));

	thor::FrameAnimation jump;

	addFrames(jump, 1, 1, 4);

	animator.addAnimation("jump", jump, sf::seconds(1.0f));

	
	float width = sprite.getLocalBounds().width/2;
	float height = sprite.getLocalBounds().height/8;
	
	
	
	b2BodyDef bodyDef;
	bodyDef.position = b2Vec2((x)/window->getScale(), (y)/window->getScale());
	bodyDef.type = b2_dynamicBody;
    body = world->CreateBody(&bodyDef);

	b2PolygonShape shape;
	shape.SetAsBox((width*0.5/2)/window->getScale(), (height*0.8/2)/window->getScale()); // Creates a box shape. Divide your desired width and height by 2.
    b2FixtureDef fixtureDef;
    fixtureDef.density = 2.f;
    fixtureDef.friction = 0.4f;
    fixtureDef.shape = &shape;
	fixtureDef.filter.categoryBits = CHARACTER;
	fixtureDef.filter.maskBits = GROUND | NPCBODY;
    body->CreateFixture(&fixtureDef);
	body->SetFixedRotation(true);


	b2FixtureDef sensor;
	sensor.density = 2.f;
	sensor.friction = 0.4f;

	shape.SetAsBox(width*0.5/2/window->getScale(), 2/window->getScale(), b2Vec2(0/window->getScale(), (height*0.8/2)/window->getScale()), 0);
	sensor.isSensor = true;
	sensor.shape = &shape;
	sensor.filter.categoryBits = CHARACTER;
	sensor.filter.maskBits = GROUND | NPCBODY;
	b2Fixture* footSensorFixture = body->CreateFixture(&sensor);
	footSensorFixture->SetUserData((void*)3);

	
	body->ApplyLinearImpulse(b2Vec2(20, 0), body->GetWorldCenter());


	goingRight = true;
	jumping = false;
}


Character::~Character(void)
{
}

void Character::drawCharacter()
{

	if(body->GetLinearVelocity().x < 0 && goingRight)
	{
		sprite.scale(-1.f, 1.f);
		goingRight = !goingRight;
	}
	else if (body->GetLinearVelocity().x > 0 && !goingRight)
	{
		sprite.scale(-1.f, 1.f);
		goingRight = !goingRight;
	}

	float width = sprite.getLocalBounds().width;
	float height = sprite.getLocalBounds().height;

	if (goingRight)
	{
		sprite.setPosition((window->getScale() * body->GetPosition().x) - (width/2), (window->getScale() * body->GetPosition().y) - (height/2));
	}
	else
	{
		sprite.setPosition((window->getScale() * body->GetPosition().x) + (width/2), (window->getScale() * body->GetPosition().y) - (height/2));
	}


	if(body->GetLinearVelocity().LengthSquared() > 0 && !animator.isPlayingAnimation())
	{
		animator.playAnimation("walk", true);
	}

	if(!animator.isPlayingAnimation())
		animator.playAnimation("stand");

	if (jumping)
	{
		animator.playAnimation("jump");
		jumping = false;
	}
		

	animator.update(clock.restart());
	animator.animate(sprite);


	window->draw(sprite);

	//sf::RectangleShape sprite2(sf::Vector2f(width, height));
	//sprite2.setFillColor(window->getBackground()->filter(sf::Color::White));
	//sprite2.setOutlineThickness(1);
	//sprite2.setOutlineColor(sf::Color::Black);
 //   sprite2.setOrigin(width/2, height/2);
	//sprite2.setPosition((window->getScale() * body->GetPosition().x), (window->getScale() * body->GetPosition().y));
 //   sprite2.setRotation(body->GetAngle() * 180/b2_pi);
	////Sprite.
 //   window->draw(sprite2);
}

void Character::addForce(sf::Vector2i loc)
{
	loc.x = loc.x + window->getOffset();
	if (!lockMovement && body->GetLinearVelocity().LengthSquared() < (K_MAX_SPEED*K_MAX_SPEED))
	{
		
		if (loc.x > body->GetPosition().x*window->getScale())
		{
			body->ApplyLinearImpulse(b2Vec2(1, 0), body->GetWorldCenter());
		}
		else
		{
			body->ApplyLinearImpulse(b2Vec2(-1, 0), body->GetWorldCenter());
		}
	}
	else if (lockMovement)
	{
		body->ApplyLinearImpulse(b2Vec2(0, 10), body->GetWorldCenter());
	}
	
	//body->ApplyForce(b2Vec2(20, 0), body->GetWorldCenter());
}

void Character::jump()
{
	//std::stringstream ss;

	//	ss << numFootContacts << "\n";
	//	OutputDebugString(ss.str().c_str());
	if (!lockMovement && numFootContacts > 0)
	{
		jumping = true;
		body->ApplyLinearImpulse(b2Vec2(0, -40), body->GetWorldCenter());
	}
}

float Character::getRadius()
{
	float width = sprite.getLocalBounds().width/4;
	float height = sprite.getLocalBounds().height/8;

	if (width > height)
	{
		return width*3;
	}
	return height*3;
}


bool Character::checkNPC(NPC* npc, bool teleport)
{
	if (!lockMovement && !npc->isComplete())
	{
		float npcRadiusSquared = std::pow(npc->getRadius(), 2);
		b2Vec2 npcPosition = npc->getPosition();

		float distanceSquared = std::pow(npc->getPosition().x-getPosition().x, 2)+std::pow(npc->getPosition().y-getPosition().y, 2);

		float characterRSquared = std::pow(getRadius(), 2);

		//std::stringstream ss;

		//ss << window->getScale() * body->GetPosition().x << "\n";
		//ss << ground->getTop() << "\n";
		//OutputDebugString(ss.str().c_str());

		if (teleport)
		{
			//characterRSquared;
			//npcRadiusSquared;
		}

		if (distanceSquared <= characterRSquared+npcRadiusSquared)
		{
			body->SetLinearVelocity(b2Vec2(0, 0));
			npc->startMessages();
			return true;
		}
	}

	return false;
	//if (getPosition().x < 200)
	//{
	//	body->SetLinearVelocity(b2Vec2_zero);
	//}
	
}

b2Vec2 Character::getPosition()
{
	return b2Vec2(body->GetPosition().x*window->getScale(), body->GetPosition().y*window->getScale());
}


void Character::unlockMovement()
{
	lockMovement = false;
}

void Character::addNumFootContacts(int n)
{
	numFootContacts += n;
}


void Character::setPosition(sf::Vector2f pos)
{

	body->SetTransform(b2Vec2(pos.x/window->getScale(), pos.y/window->getScale()), body->GetAngle());

}


// Adds a range of frames, assuming they are aligned as rectangles in the texture.
// animation:      FrameAnimation to modify
// x:              Column index of the texture rectangle
// [yFirst,yLast]: Bounds for row indices (if yLast < yFirst, add frames in reverse order)
// duration:       Relative duration of current frame (1 by default)
void Character::addFrames(thor::FrameAnimation& animation, int x, int yFirst, int yLast, float duration)
{
	const int step = (yFirst < yLast) ? +1 : -1;
	yLast += step; // so yLast is excluded in the range

	for (int y = yFirst; y != yLast; y += step)
		animation.addFrame(duration, sf::IntRect(32*x, 64*y, 32, 64));
}

void Character::stopMovement()
{
	lockMovement = true;
}

bool Character::canMove()
{
	return !lockMovement;
}